package com.chemyoo;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.Validate;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * @author Author : jianqing.liu
 * @version version : created time：2018年11月8日 下午3:13:45
 * @since since from 2018年11月8日 下午3:13:45 to now.
 * @description class description 反射获取类属性工具类
 */
public class AttributesUtils
{

	private AttributesUtils() throws NoSuchMethodException
	{
		throw new NoSuchMethodException("AttributesUtils can not be instansed");
	}

	private static final String GET = "get";

	/**
	 * 从对象中获取指定的属性值
	 * 
	 * @param obj
	 * @param fieldName
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getValue(Object obj, String fieldName)
	{
		T value = null;
		if (obj != null)
		{
			Validate.notBlank(fieldName);
			Class<?> clazz = obj.getClass();
			try
			{
				if (obj instanceof java.util.Map)
				{
					Method method = clazz.getMethod(GET, Object.class);
					return (T) method.invoke(obj, fieldName);
				}
				Field field = getFieldMap(clazz).get(fieldName.toLowerCase());
				if (field == null)
				{
					return value;
				}
				boolean accessible = field.isAccessible();
				if (!accessible)
				{
					field.setAccessible(!accessible);
				}
				value = (T) field.get(obj);
				if (!accessible)
				{
					field.setAccessible(accessible);
				}
			}
			catch (IllegalAccessException | SecurityException | NoSuchMethodException | InvocationTargetException e)
			{
				throw new IllegalArgumentException(e);
			}
		}
		return value;
	}

	/**
	 * get Fields from class and include super.
	 * 
	 * @param clazz
	 * @param annotations
	 * @return
	 */
	@SafeVarargs
	public static List<Field> getFieldsWithAnnotation(Class<?> clazz, Class<? extends Annotation>... annotations)
	{
		List<Field> fields = Lists.newArrayList();
		do
		{
			findFields(clazz, fields, annotations);
			clazz = clazz.getSuperclass();
		} while (clazz != null && clazz != Object.class);
		return fields;
	}

	public static Map<String, Field> getFieldMap(Class<?> clazz)
	{
		return convert2FieldMap(getFieldsWithAnnotation(clazz));
	}

	public static Map<String, Field> convert2FieldMap(List<Field> fields)
	{
		Map<String, Field> fieldMap = Maps.newHashMap();
		if (fields.isEmpty())
		{
			for (Field field : fields)
			{
				fieldMap.put(field.getName().toLowerCase(), field);
			}
		}
		return fieldMap;
	}

	@SafeVarargs
	private static void findFields(Class<?> clazz, List<Field> fields, Class<? extends Annotation>... annotations)
	{
		Field[] thisFields = clazz.getDeclaredFields();
		int c = annotations.length;
		for (Field field : thisFields)
		{
			if (c == 0 && !Modifier.isFinal(field.getModifiers()) && !Modifier.isStatic(field.getModifiers()))
			{
				// add neither final nor static field.
				fields.add(field);
			}
			else
			{
				for (Class<? extends Annotation> annotation : annotations)
				{
					if (field.isAnnotationPresent(annotation))
					{
						fields.add(field);
					}
				}
			}
		}
	}

}
